 ///
 /// @file    CowString.cc
 /// @author  lemon(haohb13@gmail.com)
 /// @date    2023-04-28 16:23:59
 ///
 
#include <string.h>
#include <iostream>
using std::cout;
using std::endl;
using std::ostream;

class CowString
{
	//代理类, 代理char类型的功能
	class CharProxy
	{
	public:
		//在嵌套类内部访问外部类对象的成员，还是
		//必须要通过传递参数的方式进行，不能直接
		//访问外部类对象的成员
		CharProxy(CowString & self, size_t idx)
		: _self(self)
		, _idx(idx)
		{}

		//类型转换函数:将CharProxy类型转换为char类型
		operator char()
		{	return _self._pstr[_idx];	}

		char & operator=(char ch);
	
	private:
		CowString & _self;//字符串对象本身
		size_t _idx;//下标
	};
		
	
public:
	CowString()
	: _pstr(new char[5]() + 4)
	{
		//初始化引用计数
		initRefcount();
	}

	CowString(const char * pstr)
	: _pstr(new char[strlen(pstr) + 5]() + 4)
	{
		initRefcount();
		strcpy(_pstr, pstr);
	}

	CowString(const CowString & rhs)
	: _pstr(rhs._pstr)
	{
		increaseRefcount();
	}

	CowString & operator=(const CowString & rhs)
	{
		if(this != &rhs) {//自复制
			release();//回收左操作数空间

			_pstr = rhs._pstr;//浅拷贝
			increaseRefcount();//引用计数加1
		}
		return *this;
	}

	void release() 
	{
		//引用计数减1
		decreaseRefcount();
		if(getRefcount() == 0) {
			delete [] (_pstr - 4);
			cout << "delete heap space" << endl; 
		}
	}

	~CowString() 
	{	release();	}

	int getRefcount() const
	{	return *(int*)(_pstr - 4);	}

	friend std::ostream & operator<<(std::ostream & os, 
			const CowString & rhs);

	size_t size() const {	return strlen(_pstr);	}

	const char * c_str() const {	return _pstr;	}

	//从目前的需求来看，下标访问运算符不能返回char类型的引用
	//必须返回一个自定义类类型, 而该自定义类类型是专为
	//CowString来服务的，因此还应该是一个嵌套类
	//char & operator[](size_t idx);
	CharProxy operator[](size_t idx)
	{	return CharProxy(*this, idx);	}

	const char & operator[](size_t idx) const
	{	return _pstr[idx];	}

private:
	void initRefcount()
	{	*(int*)(_pstr - 4) = 1;		}	

	void increaseRefcount() {
		++*(int*)(_pstr - 4);
	}

	void decreaseRefcount() {
		--*(int*)(_pstr - 4);
	}


private:
	char * _pstr;//永远指向的是字符串的内容
};

std::ostream & operator<<(std::ostream & os, const CowString & rhs)
{
	os << rhs._pstr;
	return os;
}

//该函数的作用是执行写操作
char & CowString::CharProxy::operator=(char ch)
{
	if(_idx < _self.size()) {
		if(_self.getRefcount() > 1) {
			_self.decreaseRefcount();
			char * ptmp = new char[_self.size() + 5]() + 4;
			strcpy(ptmp, _self._pstr);
			_self._pstr = ptmp;
			_self.initRefcount();
		}
		_self._pstr[_idx] = ch;
		return _self._pstr[_idx];
	} else {
		cout << "下标越界" << endl;
		static char nullchar = '\0';
		return nullchar;
	}
}
 
void test0() 
{
	CowString s0;
	CowString s1 = s0;
	cout << "s0's refcount:" << s0.getRefcount() << endl;
	cout << "s1's refcount:" << s1.getRefcount() << endl;
	cout << "s0:" << s0 << endl << endl;

	CowString s2("hello");
	cout << "s2:" << s2 << endl;
	cout << "s2' refcount:" << s2.getRefcount() << endl;
	CowString s3 = s2;//拷贝构造
	cout << "s2:" << s2 << endl;
	cout << "s2' refcount:" << s2.getRefcount() << endl;

	printf("s2's c_str: %p\n", s2.c_str());
	cout << "s3:" << s3 << endl;
	cout << "s3' refcount:" << s3.getRefcount() << endl;
	printf("s3's c_str: %p\n", s3.c_str());

	CowString s4("world");
	cout << "\ns4:" << s4 << endl;
	cout << "s4' refcount:" << s4.getRefcount() << endl;

	cout << "执行s4 = s2 操作" << endl;
	s4 = s2;//赋值操作
	cout << "s3:" << s3 << endl;
	cout << "s3' refcount:" << s3.getRefcount() << endl;
	printf("s3's c_str: %p\n", s3.c_str());
	cout << "s4:" << s4 << endl;
	cout << "s4' refcount:" << s4.getRefcount() << endl;
	printf("s4's c_str: %p\n", s4.c_str());

	cout << "\n执行写操作:" << endl;
	(s4[0] = 'H');
	cout << "s3:" << s3 << endl;
	cout << "s3' refcount:" << s3.getRefcount() << endl;
	printf("s3's c_str: %p\n", s3.c_str());
	cout << "s4:" << s4 << endl;
	cout << "s4' refcount:" << s4.getRefcount() << endl;
	printf("s4's c_str: %p\n", s4.c_str());

	cout << "\n执行读操作:" << endl;
	//s3和s4都是非const对象，在const版本和非const版本的
	//下标访问运算符都存在时，只能访问到非const版本
	cout << "s3[0]:" << s3[0] << endl;
	cout << "s3:" << s3 << endl;
	cout << "s3' refcount:" << s3.getRefcount() << endl;
	printf("s3's c_str: %p\n", s3.c_str());
	cout << "s2:" << s2 << endl;
	cout << "s2' refcount:" << s2.getRefcount() << endl;
	printf("s2's c_str: %p\n", s2.c_str());
 
} 
 
int main(void)
{
	test0();
	return 0;
}
